package com.reliable.service.serviceimpl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.crypto.digest.BCrypt;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.reliable.api.dto.user.ReliableUserLite;
import com.reliable.api.param.user.UserUpdateParam;
import com.reliable.api.param.user.UserUpdatePasswordParam;
import com.reliable.common.constant.Constants;
import com.reliable.common.core.domain.AjaxResult;
import com.reliable.common.utils.DateUtils;
import com.reliable.common.utils.ip.IpUtils;
import com.reliable.framework.utils.JwtUtils;
import com.reliable.framework.web.service.TokenService;
import com.reliable.service.domain.ReliableInvite;
import com.reliable.service.domain.ReliableUser;
import com.reliable.service.enums.*;
import com.reliable.service.mapper.ReliableUserMapper;
import com.reliable.api.param.user.LoginUserPasswordParam;
import com.reliable.api.param.user.UserSaveParam;
import com.reliable.service.service.IReliableInviteService;
import com.reliable.service.service.IReliableUserService;
import io.jsonwebtoken.lang.Collections;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Service;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;

import javax.annotation.Resource;

import java.util.Date;
import java.util.List;
import java.util.Objects;

/**
 * 用户列Service业务层处理
 * 
 * @author ruoyi
 * @date 2025-07-09
 */
@Service
public class ReliableUserServiceImpl extends ServiceImpl<ReliableUserMapper, ReliableUser> implements IReliableUserService {

    @Resource
    private IReliableInviteService reliableInviteService;

    @Resource
    private TokenService tokenService;



    @Override
    public AjaxResult checkUser(UserSaveParam param) {
        String account = param.getAccount();
        String password = param.getPassword();
        String confirm_password = param.getConfirm_password();
        String inviteCode = param.getInvite_code();
        String userName = param.getUserName();


        ReliableUser reliableUser = new ReliableUser();
        BeanUtil.copyProperties(param, reliableUser);

        // 判断用户是否存在
        ReliableUser user = this.getOne(new LambdaQueryWrapper<ReliableUser>().eq(ReliableUser::getAccount, account));
        if (user != null)
        {
            return AjaxResult.warn("用户已存在");
        }
        if (!password.equals(confirm_password))
        {
            return AjaxResult.warn("密码不一致");
        }
        if (StringUtils.isEmpty(inviteCode))
        {
            return AjaxResult.warn("请输入邀请码");
        }
        // 验证邀请码
        ReliableInvite inviteUser = reliableInviteService.getOne(new LambdaQueryWrapper<ReliableInvite>()
                .eq(ReliableInvite::getInviteCode, inviteCode));

        if (Objects.equals(inviteUser.getStatus(), ReliableInviteUseStatus.USED.getCode()))
        {
            return AjaxResult.warn("邀请码已使用");
        }

        if (inviteUser == null)
        {
            return AjaxResult.warn("邀请码错误");
        }

        if (inviteUser.getStatus()== InviteStatusEnum.EXPIRED.getCode()){
            return AjaxResult.warn("邀请码已过期");
        }

        reliableUser.setExpireTime(inviteUser.getExpireTime());
        String hashed = BCrypt.hashpw(password, BCrypt.gensalt());
        reliableUser.setPasswordHash(hashed);
        reliableUser.setUserName(userName);
        reliableUser.setLoginIp(IpUtils.getIpAddr());
        reliableUser.setProvince(inviteUser.getProvince());
        reliableUser.setCity(inviteUser.getCity());
        reliableUser.setDistrict(inviteUser.getDistrict());
        reliableUser.setRoleId(inviteUser.getRoleId());
        boolean save = this.save(reliableUser);

        if (!save)
        {
            return AjaxResult.error("注册失败");
        }
        inviteUser.setUserId(reliableUser.getId());
        inviteUser.setAccount(reliableUser.getAccount());
        inviteUser.setUseStatus(ReliableInviteUseStatus.USED.getCode());
        inviteUser.setStatus((long) InviteStatusEnum.VALID.getCode());
        inviteUser.setUseTime(new Date());
        reliableInviteService.updateById(inviteUser);
        return AjaxResult.success("注册成功");
    }

    @Override
    public AjaxResult loginByUserPassword(LoginUserPasswordParam param) {
        String account = param.getAccount();
        String password = param.getPassword();

        if (StrUtil.isEmpty(account) || StrUtil.isEmpty(password)) {
            return AjaxResult.warn("账号和密码不能为空");
        }

        LambdaQueryWrapper<ReliableUser> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(ReliableUser::getAccount, account);

        List<ReliableUser> reliableUsers = baseMapper.selectList(queryWrapper);

        if (Collections.isEmpty(reliableUsers)) {
            return AjaxResult.warn("账号或密码错误");
        }

        ReliableUser reliableUser = reliableUsers.get(0);

        // 检查账户是否被锁定
        if (isAccountLocked(reliableUser)) {
            long remainingTime = getRemainingLockTime(reliableUser);
            return AjaxResult.warn("账户已被锁定，请" + remainingTime + "分钟后重试");
        }

        // 验证密码
        if (!BCrypt.checkpw(password, reliableUser.getPasswordHash())) {
            // 密码错误，增加失败次数
            handleLoginFailure(reliableUser);
            return AjaxResult.warn("账号或密码错误");
        }

        // 检查账户是否过期
        if (reliableUser.getExpireType() != UserExpireType.NOT_EXPIRED.getValue()) {
            handleLoginFailure(reliableUser); // 也计入失败次数
            return AjaxResult.warn("账号或密码错误");
        }

        // 检查账户是否被封禁
        if (Objects.equals(reliableUser.getBannedType(), ReliableBannedType.BANNED.getCode())) {
            handleLoginFailure(reliableUser); // 也计入失败次数
            return AjaxResult.warn("账号或密码错误");
        }

        // 登录成功，重置失败次数
        resetLoginFailureCount(reliableUser);

        // 登录ip和登录时间
        recordLoginInfo(reliableUser.getId());
        ReliableUserLite userLite = new ReliableUserLite();
        BeanUtil.copyProperties(reliableUser, userLite);
        String token = JwtUtils.generateToken(userLite);

        return AjaxResult.success().put("user", reliableUser).put(Constants.TOKEN, token);
    }

    /**
     * 检查账户是否被锁定
     * @param user 用户对象
     * @return 是否被锁定
     */
    private boolean isAccountLocked(ReliableUser user) {
        // 检查是否有失败记录
        if (user.getLoginFailCount() == null || user.getLoginFailCount() <= 0) {
            return false;
        }

        // 检查失败次数是否达到限制（5次）
        if (user.getLoginFailCount() >= 5) {
            // 检查锁定时间是否已过（10分钟）
            if (user.getLastLoginFailTime() != null) {
                long currentTime = System.currentTimeMillis();
                long lastFailTime = user.getLastLoginFailTime().getTime();
                // 锁定10分钟（600000毫秒）
                if (currentTime - lastFailTime < 600000) {
                    return true; // 仍在锁定期内
                } else {
                    // 锁定时间已过，自动重置失败次数
                    resetLoginFailureCount(user);
                }
            }
        }
        return false;
    }

    /**
     * 获取剩余锁定时间（分钟）
     * @param user 用户对象
     * @return 剩余分钟数
     */
    private long getRemainingLockTime(ReliableUser user) {
        if (user.getLastLoginFailTime() == null) {
            return 0;
        }

        long currentTime = System.currentTimeMillis();
        long lastFailTime = user.getLastLoginFailTime().getTime();
        long passedTime = currentTime - lastFailTime;
        long remainingTime = (600000 - passedTime) / 60000; // 转换为分钟

        return Math.max(0, remainingTime);
    }

    /**
     * 处理登录失败
     * @param user 用户对象
     */
    private void handleLoginFailure(ReliableUser user) {
        // 增加失败次数
        int failCount = user.getLoginFailCount() != null ? user.getLoginFailCount() : 0;
        failCount++;

        user.setLoginFailCount(failCount);
        user.setLastLoginFailTime(new Date());

        // 更新数据库
        baseMapper.updateById(user);
    }

    /**
     * 重置登录失败次数
     * @param user 用户对象
     */
    private void resetLoginFailureCount(ReliableUser user) {
        user.setLoginFailCount(0);
        user.setLastLoginFailTime(null);

        // 更新数据库
        baseMapper.updateById(user);
    }

    public void recordLoginInfo(Long userId) {
        ReliableUser reliableUser = new ReliableUser();
        reliableUser.setId(userId);
        reliableUser.setLoginIp(IpUtils.getIpAddr());
        reliableUser.setLoginTime(DateUtils.getNowDate());
        // 登录成功时也重置失败次数
        reliableUser.setLoginFailCount(0);
        reliableUser.setLastLoginFailTime(null);
        baseMapper.updateById(reliableUser);
    }


    @Override
    public AjaxResult update(UserUpdateParam param) {
        // 获取当前用户
        ReliableUserLite currentUser = JwtUtils.getCurrentUser();
        if (currentUser == null) {
            return AjaxResult.error("用户未登录");
        }

        // 完善修改的信息
        Long userId = param.getId();

        // 权限检查：用户只能修改自己的信息
        if (!currentUser.getId().equals(userId)) {
            return AjaxResult.error("无权限修改其他用户信息");
        }

        ReliableUser reliableUser = baseMapper.selectById(userId);
        if (reliableUser == null) {
            return AjaxResult.warn("用户不存在");
        }

        // 把 param中的信息赋值给reliableUser
        BeanUtil.copyProperties(param, reliableUser);
        //修改reliableuser
        boolean update = baseMapper.updateById(reliableUser) > 0;
        return update ? AjaxResult.success("修改成功") : AjaxResult.warn("修改失败");
    }


    @Override
    public AjaxResult updatePassword(UserUpdatePasswordParam param) {
        // 获取当前用户
        ReliableUserLite currentUser = JwtUtils.getCurrentUser();
        if (currentUser == null) {
            return AjaxResult.error("用户未登录");
        }

        Long id = param.getId();
        // 如果参数中没有提供id，则使用当前用户的id
        if (id == null) {
            id = currentUser.getId();
        }

        // 权限检查：用户只能修改自己的密码
        if (!currentUser.getId().equals(id)) {
            return AjaxResult.error("无权限修改其他用户密码");
        }

        String password = param.getPassword();
        String newPassword = param.getNew_password();
        String confirmPassword = param.getConfirm_password();

        ReliableUser reliableUser = baseMapper.selectById(id);
        if (reliableUser == null) {
            return AjaxResult.warn("用户不存在");
        }

        // 判断原密码是否一致
        if (!BCrypt.checkpw(password, reliableUser.getPasswordHash())) {
            return AjaxResult.warn("原密码错误");
        }
        // 判断确认密码和新密码是否一致
        if (!newPassword.equals(confirmPassword)){
            return AjaxResult.warn("新密码与确认密码不一致");
        }
        // 不能修改和原密码一致
        if (newPassword.equals(password)) {
            return AjaxResult.warn("新密码不能与旧密码相同");
        }
        String hashed = BCrypt.hashpw(newPassword, BCrypt.gensalt());
        reliableUser.setPasswordHash(hashed);
        boolean update = baseMapper.updateById(reliableUser) > 0;
        return update ? AjaxResult.success("修改成功") : AjaxResult.warn("修改失败");
    }



}
